//
//  OPHNetwork.m
//  Pods
//
//  Created by lingen on 16/3/24.
//
//

#import "OPHNetwork.h"
#import "OPHRequest.h"
#import <AFNetworking/AFNetworking.h>
#import "OPHUploadRequest.h"
#import "OPHUploadRequestExecute.h"
#import "OPHDownloadRequest.h"
#import "OPHDownloadRequestExecute.h"

/**
 *  GET请求头
 */
static NSString* HTTP_GET = @"GET";

/**
 *  POST请求头
 */
static NSString* HTTP_POST = @"POST";

/**
 *  PUT请求头
 */
static NSString* HTTP_PUT = @"PUT";

/**
 *  DELETE请求头
 */
static NSString* HTTP_DELETE = @"DELETE";

/**
 *  请求头字符
 */
static NSString* CONTENT_TYPE =@"Content-Type";

/**
 *  申明接受的ACCEPT
 */
static NSString* ACCPET = @"ACCEPT";


static NSString* Content_Length = @"Content-Length";

/**
 *  JSON请求格式
 */
static NSString* JSON_CONTENT_TYPE = @"application/json";

@interface OPHNetwork()<NSURLSessionTaskDelegate>


@property (nonatomic,strong) NSMutableDictionary* uploadDic;


@property (nonatomic,strong) NSMutableDictionary* downloadDic;

/**
 *  取消序列
 */
@property (nonatomic,strong) NSMutableArray* cancelUploadId;

/**
 *  下载取消队列
 */
@property (nonatomic,strong) NSMutableArray* cancelDownloadId;


@property (nonatomic,strong) NSMutableDictionary* pausedUloadingDics;


@property (nonatomic,strong) NSMutableDictionary* pauseDownloadingDics;


@property (nonatomic,strong) NSURLSession* session;


@property (nonatomic, strong) AFURLSessionManager *sessionManager;

@property (nonatomic,strong) NSMutableDictionary* defaultHeaderDic;

@end



/**
 *  OPHNetwork实现
 */
@implementation OPHNetwork

/**
 *  实例共享，单例
 *
 *  @return 返回OPHNetwork单例
 */
+(instancetype)sharedInstance{
    static OPHNetwork* instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      instance = [[OPHNetwork alloc] init];
      instance.uploadDic = [[NSMutableDictionary alloc] init];
      instance.cancelUploadId = [[NSMutableArray alloc] init];
      instance.downloadDic = [[NSMutableDictionary alloc] init];
      instance.cancelDownloadId = [[NSMutableArray alloc] init];
      instance.pausedUloadingDics = [[NSMutableDictionary alloc] init];
      instance.pauseDownloadingDics = [[NSMutableDictionary alloc] init];
        
        instance.defaultHeaderDic = [[NSMutableDictionary alloc] init];
        
        
      NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
      operationQueue.maxConcurrentOperationCount = 10;
        
        __weak typeof(instance) weakInstance = instance;
      instance.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:weakInstance delegateQueue:operationQueue];
        

        
        NSString *identifier = [instance backgroundSessionIdentifier];
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
        sessionConfig.allowsCellularAccess = YES;
        instance.sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfig];

    });
    return instance;
}

- (NSString *)backgroundSessionIdentifier {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSString *identifier = [NSString stringWithFormat:@"%@.Downloader", bundleId];
    return identifier;
}

+(instancetype)createInstanceWithQueue:(int)queueCount{
    OPHNetwork* instance = nil;
    instance = [[OPHNetwork alloc] init];
    instance.uploadDic = [[NSMutableDictionary alloc] init];
    instance.cancelUploadId = [[NSMutableArray alloc] init];
    instance.downloadDic = [[NSMutableDictionary alloc] init];
    instance.cancelDownloadId = [[NSMutableArray alloc] init];
    instance.pausedUloadingDics = [[NSMutableDictionary alloc] init];
    instance.pauseDownloadingDics = [[NSMutableDictionary alloc] init];
    
    
    NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
    operationQueue.maxConcurrentOperationCount = queueCount;
    
    __weak typeof(instance) weakInstance = instance;
    instance.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:weakInstance delegateQueue:operationQueue];
    return instance;
}

/**
 *  发出一个上传文件的请求
 *
 *  @param reqeust        OPHUploadRequest请求ß
 *  @param progressBlock  进度
 *  @param comepleteBlock 回调
 */
-(void)uploadRequest:(OPHUploadRequest*)uploadRequest progress:(void(^)(float progress))progressBlock comeplete:(void(^)(OPHResponse* response))comepleteBlock{
     
     OPHUploadRequestExecute* uploadExecute = [[OPHUploadRequestExecute alloc] init];
     uploadExecute.progressBlock = progressBlock;
     uploadExecute.completeBlock = comepleteBlock;
     uploadExecute.uploadRequest = uploadRequest;
     
     if (![_cancelUploadId containsObject:uploadRequest.uploadId]) {
         
         OPHUploadRequestExecute* existsUploadExecute = _uploadDic[uploadRequest.uploadId];
         if (existsUploadExecute) {
             [existsUploadExecute cancelUpload];
         }
         
          [uploadExecute startUpload];
          [_uploadDic setObject:uploadExecute forKey:uploadRequest.uploadId];
     }else{
          [_cancelUploadId removeObject:uploadRequest.uploadId];
     }

}

/*
 * 暂停一个上传行为
 */
-(BOOL)pauseUplaodRequest:(NSString*)uploadId{
     if (_pausedUloadingDics[uploadId]) {
          return YES;
     }
     
     if (_uploadDic[uploadId]) {
          OPHUploadRequestExecute* uploadExecute = _uploadDic[uploadId];
          [uploadExecute pauseUpload];
          [_uploadDic removeObjectForKey:uploadId];
          [_pausedUloadingDics setObject:uploadExecute forKey:uploadId];
          return YES;
     }
     
     return NO;
}

/*
 * 重启一个上传行为
 */
-(BOOL)resumeUploadRequest:(NSString*)uploadId{
     if (_pausedUloadingDics[uploadId]) {
          OPHUploadRequestExecute* uploadExecute = _pausedUloadingDics[uploadId];
          BOOL resume = [uploadExecute resumeUpload];
          if (!resume) {
               return NO;
          }
          [_uploadDic setObject:uploadExecute forKey:uploadId];
          [_pausedUloadingDics removeObjectForKey:uploadId];
          return YES;
     }
     
     return NO;
}

-(BOOL)isDownloading:(NSString*)donwloadId{
    if (_downloadDic[donwloadId]) {
        return YES;
    }
    return NO;
}

-(BOOL)isUploading:(NSString*)uploadId {
    if (!uploadId || uploadId.length == 0) {
        return NO;
    }
    
    if (_uploadDic[uploadId]) {
        return YES;
    }
    return NO;
}
/**
 *  发出一个下载请求
 *
 *  @param request        请求
 *  @param progressBlock  progress回调
 *  @param comepleteBlock 完成回调
 */
-(void)downloadRequest:(OPHDownloadRequest*)request progress:(void(^)(float progress))progressBlock comeplete:(void(^)(NSString* filePath))comepleteBlock{
     OPHDownloadRequestExecute* downloadExecute = [[OPHDownloadRequestExecute alloc] init];
     downloadExecute.progressBlock = progressBlock;
     downloadExecute.completeBlock = comepleteBlock;
     downloadExecute.downloadRequest = request;
    
     BOOL result = [self resumeDownloadRequest:request.downloadId];
     if (result) {
         return;
     }
    
     if (![_cancelDownloadId containsObject:request.downloadId]) {
        [downloadExecute startDownload];
        [_downloadDic setObject:downloadExecute forKey:request.downloadId];
     } else {
        [_cancelDownloadId removeObject:request.downloadId];
     }
}

-(BOOL)cancelAllUploads{
     
     NSArray* uploads =  [_uploadDic allValues];
     
     for (OPHUploadRequestExecute* uploadExecute  in uploads) {
          [uploadExecute pauseUpload];
          [_pausedUloadingDics setObject:uploadExecute forKey:uploadExecute.uploadRequest.uploadId];
     }
     return YES;
}


-(BOOL)cancelAllDownloads{
     NSArray* downloads = [_downloadDic allValues];
     
     for (OPHDownloadRequestExecute* downloadExecute in downloads) {
          [downloadExecute pauseDownload];
          [_pauseDownloadingDics setObject:downloadExecute forKey:downloadExecute.downloadRequest.downloadId];
     }
     return YES;
}
/**
 *  取消一个上传行为
 *
 *  @param uploadId 上传
 *
 *  @return 是否取消成功
 */
-(BOOL)cancelUploadRequest:(NSString*)uploadId{
     OPHUploadRequestExecute* uploadExecute = _uploadDic[uploadId];
     if (uploadExecute) {
          [uploadExecute cancelUpload];
     }else{
          [_cancelUploadId addObject:uploadId];
     }
     return YES;
}


/**
 *  取消一个下载行为
 *
 *  @param downloadId download ID
 *
 *  @return 返回是否取消成功
 */
-(BOOL)cancelDownloadRequest:(NSString*)downloadId{
     OPHDownloadRequestExecute* downloadExecute = _downloadDic[downloadId];
     if (downloadExecute) {
          [downloadExecute cancelDownload];
         [_downloadDic removeObjectForKey:downloadId];
     }else{
          [_cancelDownloadId addObject:downloadId];
     }
     return YES;
}

/*
 *  暂停一个下载行为
 */
-(BOOL)pauseDownloadRequest:(NSString*)downloadId{
     if (_pauseDownloadingDics[downloadId]) {
          return YES;
     }
     
     if (_downloadDic[downloadId]) {
          OPHDownloadRequestExecute* downloadExecute = _downloadDic[downloadId];
          [downloadExecute pauseDownload];
          [_downloadDic removeObjectForKey:downloadId];
          [_pausedUloadingDics setObject:downloadExecute forKey:downloadId];
          return YES;
     }
     return NO;
}

/*
 *  重启一个下载行为
 */
-(BOOL)resumeDownloadRequest:(NSString*)downlaodId{
     if (_pausedUloadingDics[downlaodId]) {
          OPHDownloadRequestExecute* downloadExecute = _pausedUloadingDics[downlaodId];
          BOOL resume = [downloadExecute resumeDownload];
          if (!resume) {
               return NO;
          }
          [_pausedUloadingDics removeObjectForKey:downloadExecute];
          [_downloadDic setObject:downloadExecute forKey:downlaodId];
          return YES;
     }
     return NO;
}

-(OPHResponse*)syncRequest:(OPHRequest*)request{
    OPHResponse *response = [self p_syncRequest:request];
//#if DEBUG
//    OPHJsonStatusResult *responseResult = [response expectedJsonStatusResultObject];
//    NSLog(@"【网络】URL: %@ 参数: %@ 状态码: %@ 数据: %@", request.url, request.params, @(responseResult.status), responseResult.result);
//#endif
    return response;
}

/**
 *  异步请求
 *
 *  @param request     请求
 *  @param resultBlock 回调结果
 */
-(void)asyncRequest:(OPHRequest*)request resultBlock:(void (^)(OPHResponse* response))resultBlock{
     
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        OPHResponse* reponse = [self p_syncRequest:request];
        if (resultBlock) {
            resultBlock(reponse);
        }
    });
    
}


#pragma REPONSE

-(OPHResponse*)p_syncRequest:(OPHRequest*)ophRequest{
    UInt64 begin = [[NSDate date] timeIntervalSince1970] * 1000;
    [self p_checkMainThread];
    
    NSURL *URL = [NSURL URLWithString:ophRequest.url];
    if (!([[URL.scheme lowercaseString] isEqualToString:@"http"] ||
          [[URL.scheme lowercaseString] isEqualToString:@"https"])) {
        return [OPHResponse errorStatusCodeResponse:404 error:nil];
    }
    if (!URL) {
        URL = [NSURL URLWithString:[ophRequest.url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
    }
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    [request setHTTPMethod:[self p_getRequestMethod:ophRequest.type]];
    [request setTimeoutInterval:ophRequest.timeout];
    
    
//    NSDictionary* params = ophRequest.params;
     id params = ophRequest.params;
    
     if (ophRequest.type == OPH_HTTP_POST || ophRequest.type == OPH_HTTP_PUT) {
          [request setValue:JSON_CONTENT_TYPE forHTTPHeaderField:ACCPET];
     }
     
    if (params!= nil) {
        [request setValue:JSON_CONTENT_TYPE forHTTPHeaderField:CONTENT_TYPE];
        NSData *requestData = [NSJSONSerialization dataWithJSONObject:params
                                                              options:NSJSONWritingPrettyPrinted
                                                                error:nil];
        [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[requestData length]] forHTTPHeaderField:Content_Length];
        
        [request setHTTPBody: requestData];
    }
    
    for (NSString* key in _defaultHeaderDic.allKeys) {
        [request setValue:_defaultHeaderDic[key] forHTTPHeaderField:key];
    }
    
//    NSError *error = nil;
//    NSHTTPURLResponse* response = nil;
//    NSData* reponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    
    __block NSError *error = nil;
    __block NSHTTPURLResponse* response = nil;
    __block NSData* reponseData = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    
    UInt64 begin2 = [[NSDate date] timeIntervalSince1970] * 1000;
    
    __block UInt64 time3;
    [[_session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable resp, NSError * _Nullable err) {
        time3 = [[NSDate date] timeIntervalSince1970] * 1000;
        response = (NSHTTPURLResponse*)resp;
        error = err;
        reponseData = data;
        dispatch_semaphore_signal(semaphore);
    }] resume];
    
    NSTimeInterval timeout = ophRequest.timeout;
    if (timeout == 0) {
        timeout = 120;
    }
    
    dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW,  timeout * NSEC_PER_SEC);

    dispatch_semaphore_wait(semaphore, delay);
    
    
    UInt64 end  = [[NSDate date] timeIntervalSince1970] * 1000;
    
    //如果一个网络请求大于5S，则进行日志记录
    if (end - begin > 1000 * 5) {
        NSString* text = [NSString stringWithFormat:@"NETWORK MONITOR: time:%llu url:%@ params:%@ begin:%llu,time 2:%llu time 3:%llu end:%llu",(end - begin),ophRequest.url,ophRequest.params,begin,begin2,time3,end];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"WOP_DD_LOG" object:nil userInfo:@{
                                                                                                       @"text":text
                                                                                                       }];
    }
    return [self p_getOPHJSONResponse:response reponseData:reponseData error:error];
}

-(OPHResponse*)p_getOPHJSONResponse:(NSHTTPURLResponse*)response reponseData:(NSData*)reponseData error:(NSError*)error{
    int statuCode = (int)response.statusCode;
    
    OPHResponse* reponse  = nil;
    
    if (statuCode == 200) {
        reponse = [OPHResponse okReponse:reponseData];
    }else{
        reponse = [OPHResponse errorStatusCodeResponse:statuCode error:error];
        reponse.data = reponseData;
    }
    return reponse;
}

-(NSString*)p_getRequestMethod:(OPHRequestType)type{
    if (type == OPH_HTTP_GET) {
        return HTTP_GET;
    }
    
    else if(type == OPH_HTTP_POST){
        return HTTP_POST;
    }
    
    else if(type == OPH_HTTP_PUT){
        return HTTP_PUT;
    }
    
    else if(type == OPH_HTTP_DELETE) {
        return HTTP_DELETE;
    }
    return nil;
}

/**
 *  检查是否在主线程进行操作
 */
-(void)p_checkMainThread{
    BOOL isMainThread = [NSThread isMainThread];
    if (isMainThread) {
        [NSException raise:@"Network main thread exception" format:@"Network Actions Not Allow in Main Thread"];
    }
}

-(BOOL)cancelAllUnFinished{
    __weak typeof(self) weakSelf = self;
    [weakSelf.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        
        if (!dataTasks || !dataTasks.count) {
            return;
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            for (NSURLSessionTask *task in dataTasks) {
                if (task.state == NSURLSessionTaskStateRunning){
                    [task cancel];
                }
            }
        });
     
    }];
 

    return YES;
}



- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(ios(10.0)){
#ifndef DEBUG
    long mill = metrics.taskInterval.duration * 1000;
    if (mill > 3000){
//        NSString* text = [NSString stringWithFormat:@"TIME MONITOR: time:%f,info:%@",metrics.taskInterval.duration,[metrics description]];
        NSString* text = [NSString stringWithFormat:@"TIME MONITOR: time:%f",metrics.taskInterval.duration];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"WOP_DD_LOG" object:nil userInfo:@{
                                                                                                       @"text":text
                                                                                                       }];
    }
#endif
}


-(void)setDefaultHeader:(NSString*)value forKey:(NSString*)key{
    _defaultHeaderDic[key] = value;
}


-(BOOL)hasUnCompleteDownloadOrUpload{
    return _uploadDic.count > 0 || _downloadDic.count > 0;
}



@end
